#include "tunnel.h"
#include "router.h"
#include "client.h"
#include "../common/codec.h"
#include "../common/factory.h"
#include "../common/console.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>



Tunnel::Tunnel (Client* client, const Config& cfg) : _cfg(cfg), _client(client) {
  TRACE
}


void Tunnel::startup (Router* router) {
  TRACE
  abort_assert(_rsa.setup_public(_cfg.rsa_key, strlen(_cfg.rsa_key)));
  Codec::gen(_aes_key);
  abort_assert(_aes.setup(_aes_key, 256));

  _socket = socket(AF_INET, SOCK_STREAM, 0);
  abort_assert(_socket > 0, "Client.socket: create socket failed");

  struct sockaddr_in addr = {0};
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr(_cfg.server_ip);
  addr.sin_port = htons(_cfg.server_port);
  abort_assert(-1 != connect(_socket, (sockaddr*)&addr, sizeof(addr)));

  handshake();
  Console::run("route add -host %s gw %s dev %s", _cfg.server_ip, _cfg.gateway_ip, _cfg.physics_if);

  _router = router;
  _upWorker = new std::thread(&Tunnel::upLoop, this);
  _upWorker->detach();
  _downWorker = new std::thread(&Tunnel::downLoop, this);
  _downWorker->detach();
}


void Tunnel::shutdown () {
  TRACE
  Console::run("route del -host %s", _cfg.server_ip);
  delete _upWorker;
  delete _downWorker;
  close(_socket);
}


void Tunnel::upLoop () {
  TRACE
  for (;;) {
    auto pkg = _queue.poll();
    auto data = Codec::encode(pkg->buf, _aes, _aes_key);
    auto nwrite = send(_socket, data.readIter(), data.readable(), 0);
    if (nwrite <= 0) {
      log_e("Tunnel: write data error");
      Factory::executor().execute([this] () {
        _client->shutdown();
      });
      return;
    }
  }
}


void Tunnel::downLoop () {
  TRACE
  u8 buf[IpPackage::MAX_SIZE + 16];
  for (;;) {
    auto nread = recv(_socket, buf, IpPackage::MAX_SIZE + 16, 0);
    if (nread <= 0) {
      log_e("Tunnel: read data error");
      Factory::executor().execute([this] () {
        _client->shutdown();
      });
      return;
    }
    _cache_mutex.lock();
    _cache.normalize();
    _cache.write(buf, (uwl)nread);
    _cache_mutex.unlock();
    Factory::executor().execute([this] () {
      for (;;) {
        _cache_mutex.lock();
        auto data = Codec::decode(_cache, _aes, _aes_key);
        _cache_mutex.unlock();
        if (!data.available())
          break;
        auto pkg = Codec::parse(data);
//        auto pkg = Ptr<IpPackage>::create();
//        pkg->buf = std::move(data);
        if (!pkg->err.empty())
          log_e("Tunnel: package error: %p", pkg.ptr());
        if (pkg)
          _router->write(std::move(pkg));
      }
    });
  }
}


void Tunnel::handshake () {
  TRACE
  auto data = ByteBuffer<>::create(_aes_key, 32);
  auto pkg = Codec::encode(data, _rsa);
  auto nwrite = send(_socket, pkg.readIter(), pkg.readable(), 0);
  abort_assert(nwrite == (swl)pkg.readable());
}


void Tunnel::write (Ptr<IpPackage> pkg) {
  TRACE
  _queue.push(std::move(pkg));
}